#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h> 
#include <stdlib.h>       
#include <string.h>
#include <pthread.h>
//#include <sys/sem.h>
#include <errno.h>
#include <sys/ipc.h>
//#include <sys/msg.h>
#include <sys/timeb.h>
#include <sys/socket.h>
//#include <sys/socketvar.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <semaphore.h>
#include <string.h>
#include <termios.h>
#include <linux/input.h>
#include <semaphore.h>
#include <linux/shm.h>
#include <asm/unistd.h>
#include <sys/poll.h>

#include "common.h"

#define MIN_OPENING_REUTRN_BYTES 50
#define MAX_OPENING_REUTRN_BYTES 900
#define checkIfOpeningWifi(bytes) (bytes >= MIN_OPENING_REUTRN_BYTES && bytes <= MAX_OPENING_REUTRN_BYTES)


static const char routeIp[] = "192.168.1.1";
static const short routePort = 23;

static const char openCmd[] = "wifi\r\n";
static const char closeCmd[] = "wifi down\r\n";
static const char getCmd[] = "iwinfo\r\n";

static const int connectTimeout = 5;
static const int sendTimeout = 5;
static const int recvTimeout = 5;
#define  WIFI_RECV_BUF_SIZE 2048
static const int closeSockDelayTime = 2;

static const unsigned int orgTryCloseWifiDelayTimeList[] = {45, 60, 75, 90, 105, 120};
static unsigned char userFirstOpenWifi = 0;
static pthread_mutex_t lock;

typedef enum
{
	eGetWifiStatus = 0x00,
	eCloseWifi = 0x01,
	eOpenWifi = 0x02,
	eWifiActionMax,
}WifiAction;

typedef enum 
{
	eConnectRouteFail = 0x00,
	eWifiIsClose = 0x01,
	eWifiIsOpen = 0x02,
	eActResultMax,
}ActResult;

enum _protoSlaveType
{
	_getWifiStatus = 0x00,
	_setWifiStatus = 0x01,
};

static int getWifiStatus(struct recvData *recvData, struct packageFilter *pFilter);
static int setWifiStatus(struct recvData *recvData, struct packageFilter *pFilter);
static unsigned char actWifi(WifiAction action);
static int actWifiReal(WifiAction action);
static void returnBackPackage(struct recvData *recvData, struct packageFilter *pFilter, unsigned char returnRet);

static void *wifiThread(void *arg);
static void fixWifi();
static char *get_cur_time_us();

int initWifi()
{
	if( pthread_mutex_init (&lock,NULL) )
	{
		LOGD("pthread_mutex_init fail !\n");	
		return -1;
	}

	//fixWifi();

	pthread_t ptID;
  if( pthread_create(&ptID, NULL, wifiThread, (void *)0) != 0 )
	{
			char logBuf[256];
			snprintf(logBuf, 256, "create thread failure\n");
			LOGD(logBuf);
			return -2;
	}

	return 0;
}

static void *wifiThread(void *arg)
{
	//fixWifi();
	sleep(120);
	openWifi();
	sleep(30);
	openWifi();
	sleep(30);
	openWifi();
	
	return NULL;
}

static void fixWifi()
{
#ifdef FIX_WIFI
	char logBuf[256];
	unsigned char i, ret;
	unsigned char aimRet = (unsigned char)eWifiIsClose;
	for(i = 0; i < sizeof(orgTryCloseWifiDelayTimeList) / sizeof(unsigned int); i ++)
	{
		if(i == 0)
		{
			sleep(orgTryCloseWifiDelayTimeList[i]);
		}
		else
		{
			sleep(orgTryCloseWifiDelayTimeList[i] - orgTryCloseWifiDelayTimeList[i - 1]);	
		}
		
		if(userFirstOpenWifi)
		{
			sprintf(logBuf, "%u:we do not need to close wifi at the beginning of start due to user has opened the projector !\n", orgTryCloseWifiDelayTimeList[i]);
			writeLogEx(logBuf);
			break;
		}
		else
		{
			sprintf(logBuf, "%u:we do need to close wifi at the beginning of start due to user has not opened the projector !\n", orgTryCloseWifiDelayTimeList[i]);
			writeLogEx(logBuf);
			ret = closeWifi();
			if(ret == aimRet)
			{
				sprintf(logBuf, "%u:org close wifi success !\n", orgTryCloseWifiDelayTimeList[i]);
				writeLogEx(logBuf);
				break;
			}
		}
	}
#endif
}


void wifiServer(struct recvData *recvData)
{
	if (recvData == NULL)
	{
		writeLogEx("recvData == NULL \n");
		return;
	}

	struct packageFilter filter;
	int ret = explainPackageHead(recvData, &filter);
	if (ret < 0)
	{
		return;
	}

	switch (filter.protoSlaveType)
	{
	case _getWifiStatus:
		getWifiStatus(recvData, &filter);
		break;
	case _setWifiStatus:
		setWifiStatus(recvData, &filter);
		break;
	default:
		char logBuf[256];
		sprintf(logBuf, "protoType not right ! protoMasterType = %d, protoSlaveType = %d\n", filter.protoMasterType, filter.protoSlaveType);
		writeLogEx(logBuf);
		break;
	}
}

static int getWifiStatus(struct recvData *recvData, struct packageFilter *pFilter)
{
	printf("into getWifiStatus\n");
	char logBuf[256];
	if (pFilter->dataDirection != 0x00)
	{
		sprintf(logBuf, "proto %d,%d pFilter->dataDirection error ! pFilter->dataDirection = %d\n", pFilter->protoMasterType, pFilter->protoSlaveType, pFilter->dataDirection);
		writeLogEx(logBuf);
		return -1;
	}

	if (pFilter->dataLen != 4)
	{
		sprintf(logBuf, "proto d,%d pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->protoMasterType, pFilter->protoSlaveType, pFilter->dataLen);
		writeLogEx(logBuf);
		return -2;
	}

	if (&(recvData->addr) == NULL)
	{
		sprintf(logBuf, "recvData->addr == NULL !\n");
		writeLogEx(logBuf);
		return -3;
	}

	unsigned char ret = actWifi(eGetWifiStatus);
	sprintf(logBuf, "get wifi status reuturn %u\n", ret);
	writeLogEx(logBuf);
	returnBackPackage(recvData, pFilter, ret);
	return 0;
}

static int setWifiStatus(struct recvData *recvData, struct packageFilter *pFilter)
{
	printf("into setWifiStatus\n");
	char logBuf[256];
	if (pFilter->dataDirection != 0x00)
	{
		sprintf(logBuf, "proto %d,%d pFilter->dataDirection error ! pFilter->dataDirection = %d\n", pFilter->protoMasterType, pFilter->protoSlaveType, pFilter->dataDirection);
		writeLogEx(logBuf);
		return -1;
	}

	if (pFilter->dataLen != 4)
	{
		sprintf(logBuf, "proto d,%d pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->protoMasterType, pFilter->protoSlaveType, pFilter->dataLen);
		writeLogEx(logBuf);
		return -2;
	}

	if (&(recvData->addr) == NULL)
	{
		sprintf(logBuf, "recvData->addr == NULL !\n");
		writeLogEx(logBuf);
		return -3;
	}

	WifiAction action = (WifiAction)(pFilter->pData[4]);
	if (action != eOpenWifi && action != eCloseWifi)
	{
		sprintf(logBuf, "action error ! action = %u\n", action);
		writeLogEx(logBuf);
		return -4;
	}

	unsigned char ret = actWifi(action);
	sprintf(logBuf, "set wifi status %u return %u\n", (unsigned int)action, (unsigned int)ret);
	writeLogEx(logBuf);
	returnBackPackage(recvData, pFilter, ret);
	return 0;
}

static unsigned char actWifi(WifiAction action)
{
	unsigned char returnRet = 0;
	
#ifdef CTRL_WIFI
	int ret = actWifiReal(action);
	char logBuf[256];
	sprintf(logBuf, "actWifiReal return %d, action = %u\n", ret,action);
	writeLogEx(logBuf);
	if (ret < 0)
	{
		returnRet = (unsigned char)eConnectRouteFail;
	}
	else
	{
		switch (action)
		{
		case eOpenWifi:
			returnRet = (unsigned char)eWifiIsOpen;
			break;
		case eCloseWifi:
			returnRet = (unsigned char)eWifiIsClose;
			break;
		case eGetWifiStatus:
			if ((unsigned char)ret > (unsigned char)eActResultMax)
			{
				returnRet = (unsigned char)eConnectRouteFail;
			}
			else
			{
				returnRet = (unsigned char)ret;
			}
			break;
		default:
			sprintf(logBuf, "unknow action ! action = %u\n", (unsigned int)action);
			writeLogEx(logBuf);
			break;
		}
	}
#endif

	return returnRet;
}

static int actWifiReal(WifiAction action)
{
	char logBuf[256];
	if (action >= eWifiActionMax)
	{
		sprintf(logBuf, "wifi action param error ! action = %u\n", action);
		writeLogEx(logBuf);
		return -1;
	}

	int sock = connectTo(routeIp, routePort, connectTimeout);
	if (sock <= 0)
	{
		sprintf(logBuf, "connect to route fail ! %s:%u\n", routeIp, routePort);
		writeLogEx(logBuf);
		return -2;
	}

	int ret;
	static char recvBuf[WIFI_RECV_BUF_SIZE];
	int recvBytes;
	switch (action)
	{
	case eOpenWifi:
		printf("openCmd:%s,%u\n", openCmd, sizeof(openCmd));
		ret = SendData(sock, openCmd, sizeof(openCmd), sendTimeout);
		printf("openCmd send bytes = %d\n", ret);
		if (ret <= 0)
		{
			ret = -4;
		}
		else
		{
			recvBytes = RecvData(sock, recvBuf, WIFI_RECV_BUF_SIZE, recvTimeout);
			int i;
			printf("open(%d):", recvBytes);
			for(i = 0; i < recvBytes; i ++)
			{
				printf("%x ", recvBytes);	
			}
			printf("\n");
		}
		break;
	case eCloseWifi:
		printf("closeCmd:%s,%u", closeCmd, sizeof(closeCmd));
		ret = SendData(sock, closeCmd, sizeof(closeCmd), sendTimeout);
		printf("closeCmd bytes = %d\n", ret);
		if (ret <= 0)
		{
			ret = -5;
		}
		else
		{
			recvBytes = RecvData(sock, recvBuf, WIFI_RECV_BUF_SIZE, recvTimeout);
			int i;
			printf("close(%d):", recvBytes);
			for(i = 0; i < recvBytes; i ++)
			{
				printf("%x ", recvBytes);	
			}
			printf("\n");
		}
		break;
	case eGetWifiStatus:
		printf("getCmd:%s,%u", getCmd, sizeof(getCmd));
		ret = SendData(sock, getCmd, sizeof(getCmd), sendTimeout);
		printf("getCmd send bytes = %d\n", ret);
		if (ret == sizeof(getCmd))
		{
			recvBytes = RecvData(sock, recvBuf, WIFI_RECV_BUF_SIZE, recvTimeout);
			if (recvBytes < 0)
			{
				sprintf(logBuf, "RecvData when get wifi status ! ret = %d, recvBytes = %d\n", ret, recvBytes);
				writeLogEx(logBuf);
				ret = -6;
			}
			else if(!checkIfOpeningWifi(recvBytes))
			{
				sprintf(logBuf, "RecvData when get wifi status ! recvBytes == %d ! we think it is not opening wifi !\n", recvBytes);
				writeLogEx(logBuf);
				ret = (int)eWifiIsClose;
			}
			else
			{
				sprintf(logBuf, "RecvData when get wifi status ! recvBytes == %d ! we think it is opening wifi !\n", recvBytes);
				writeLogEx(logBuf);
				int i;
				printf("get(%d):", recvBytes);
				for(i = 0; i < recvBytes; i ++)
				{
					printf("%x ", recvBytes);	
				}
				printf("\n");
				ret = (int)eWifiIsOpen;
			}
		}
		else
		{
			ret = -7;
		}
		break;
	default:
		sprintf(logBuf, "unknow action ! action = %u\n", (unsigned int)action);
		writeLogEx(logBuf);
		ret = -99;
		break;
	}

	sleep(closeSockDelayTime);
	close(sock);
	return ret;
}

static void returnBackPackage(struct recvData *recvData, struct packageFilter *pFilter, unsigned char returnRet)
{
	unsigned char dataDirection = 0x01;
	unsigned short dataLen = 5;
	createProtoCombinationData(recvData->buf + 3, pFilter->deviceType, dataDirection, pFilter->communicationWay, dataLen);

	unsigned char checkCode = 0;
	unsigned int headLen = recvData->count - pFilter->dataLen;
	unsigned int packageSize = headLen + dataLen;
	for (unsigned char i = 0; i < headLen - 1; i++)
	{
		checkCode += recvData->buf[i];
	}
	recvData->buf[headLen - 1] = checkCode;

	recvData->buf[headLen + 4] = returnRet;
	MainSend(recvData->buf, packageSize, &(recvData->addr));
}


unsigned char openWifi()
{
	pthread_mutex_lock (&lock);
	//userFirstOpenWifi = 1;
	WifiAction cmd = eOpenWifi;
	char logBuf[256];
	sprintf(logBuf, "\nopenWifi before,time:%s, cmd:%u\n", get_cur_time_us(), cmd);
	writeLogEx(logBuf);
	unsigned char ret = actWifi((WifiAction)cmd);
	sprintf(logBuf, "openWifi after,time:%s, ret:%u\n", get_cur_time_us(), ret);
	writeLogEx(logBuf);
	pthread_mutex_unlock (&lock);
	return ret;
}

unsigned char closeWifi()
{
	return 0;

	pthread_mutex_lock (&lock);
	WifiAction cmd = eCloseWifi;
	char logBuf[256];
	sprintf(logBuf, "\ncloseWifi before,time:%s, cmd:%u\n", get_cur_time_us(), cmd);
	writeLogEx(logBuf);
	unsigned char ret = actWifi((WifiAction)cmd);
	sprintf(logBuf, "closeWifi after,time:%s, ret:%u\n", get_cur_time_us(), ret);
	writeLogEx(logBuf);
	pthread_mutex_unlock (&lock);
	return ret;
}

unsigned char getWifi()
{
	pthread_mutex_lock (&lock);
	WifiAction cmd = eGetWifiStatus;
	char logBuf[256];
	sprintf(logBuf, "\ngetWifi before,time:%s, cmd:%u\n", get_cur_time_us(), cmd);
	writeLogEx(logBuf);
	unsigned char ret = actWifi((WifiAction)cmd);
	sprintf(logBuf, "getWifi after,time:%s, ret:%u\n", get_cur_time_us(), ret);
	writeLogEx(logBuf);
	pthread_mutex_unlock (&lock);
	return ret;
}

static char *get_cur_time_us()
{
#define ASCII_TIME_DATA_BUF 256
	static char ascii_time_data[ASCII_TIME_DATA_BUF];
	struct timeval tv;
	gettimeofday(&tv, NULL);

	snprintf(ascii_time_data, ASCII_TIME_DATA_BUF, "%06u.%06u", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
	return ascii_time_data;
}

void setUserFirstOpenWifi()
{
	printf("setUserFirstOpenWifi\n");
	userFirstOpenWifi = 1;	
}

